﻿using System;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;

namespace SamFirm
{
    internal class Imports
    {
        private static IntPtr mod = IntPtr.Zero;

        //콘솔을 메모리에서 반환하는 메소드
        [DllImport("kernel32.dll")]
        public static extern bool FreeConsole();

        //모듈(라이브러리)을 메모리에서 반환하는 메소드 
        [DllImport("kernel32.dll")]
        private static extern bool FreeLibrary(IntPtr hModule);
        public static void FreeModule()
        {
            if (mod != IntPtr.Zero)
            {
                if (!FreeLibrary(mod))
                {
                    Logger.WriteLine("Error FreeModule(): Unable to free library.");
                }
                mod = IntPtr.Zero;
            }
        }

        //권한을 얻는 메소드
        public static string GetAuthorization(string Nonce)
        {
            if ((mod == IntPtr.Zero) && (LoadModule("AgentModule.dll") != 0))
            {
                return string.Empty;
            }
            Auth_t _t = load_function<Auth_t>(mod, "?MakeAuthorizationHeaderWithGeneratedNonceValueAndAMModule@AgentNetworkModule@@CAPB_WPB_W@Z");
            IntPtr nonce = Marshal.StringToHGlobalUni(Nonce);
            string str = Marshal.PtrToStringUni(_t(nonce));
            Marshal.FreeHGlobal(nonce);
            return str;
        }

        [DllImport("kernel32.dll")]
        private static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
        private static T load_function<T>(IntPtr module, string name) where T: class => 
            (Marshal.GetDelegateForFunctionPointer(GetProcAddress(module, name), typeof(T)) as T);

        //모듈(라이브러리)을 로드하는 메소드
        [DllImport("kernel32.dll", CharSet=CharSet.Unicode, SetLastError=true)]
        private static extern IntPtr LoadLibrary(string dllToLoad);
        private static int LoadModule(string module)
        {
            try
            {
                string modulePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
                modulePath = Path.Combine(modulePath, "DLL", module);
                if (!File.Exists(modulePath))
                {
                    Logger.WriteLine("Error LoadModule(): " + module + " does not exist.");
                    return 1;
                }

                mod = LoadLibrary(modulePath);
                if (mod == IntPtr.Zero)
                {
                    Logger.WriteLine("Error LoadLibrary(): " + Marshal.GetLastWin32Error().ToString());
                    Logger.WriteLine("Please make sure \"Microsoft Visual C++ 2008 Redistributable Package (x86)\" and \"Microsoft Visual C++ 2010 Redistributable Package (x86)\" are installed.");
                    return 1;
                }
            }
            catch (Exception exception)
            {
                Logger.WriteLine("Error LoadModule() -> " + exception);
                return 1;
            }
            return 0;
        }

        //스레드 실행 상태를 설정하는 메소드
        [DllImport("kernel32.dll", CharSet=CharSet.Unicode, SetLastError=true)]
        public static extern EXECUTION_STATE SetThreadExecutionState(EXECUTION_STATE esFlags);

        [UnmanagedFunctionPointer(CallingConvention.Winapi)]
        private delegate IntPtr Auth_t(IntPtr nonce);

        public enum EXECUTION_STATE : uint
        {
            ES_AWAYMODE_REQUIRED = 0x40,
            ES_CONTINUOUS = 0x80000000,
            ES_DISPLAY_REQUIRED = 2,
            ES_SYSTEM_REQUIRED = 1
        }
    }
}